1. Field
The following relates generally to ray tracing systems that can be used, for example, in rendering 2-D representations of 3-D scenes, and more specifically to handling self-intersection effects due to limited precision calculations.
2. Related Art
Rendering photo-realistic 2-D images from 3-D scene descriptions with ray tracing is well-known in the computer graphics arts. Ray tracing usually involves obtaining a scene description composed of geometric shapes, which describe surfaces of structures in the scene, and can be called primitives. A common primitive shape is a triangle.
Virtual rays of light are traced into the scene from a view point (“a camera”); each ray is issued to travel through a respective pixel of the 2-D representation, on which that ray can have an effect. The rays are tested for intersection with scene primitives to identify a closest intersected primitive for each ray, if any.
After identifying an intersection for a given ray, a shader associated with that primitive determines what happens next. For example, if the primitive is part of a mirror, then a reflection ray is issued to determine whether light is hitting the intersected point from a luminaire, or in more complicated situations, subsurface reflection, and scattering can be modeled, which may cause issuance of different rays to be intersection tested. By further example, if a surface of an object were rough, not smooth, then a shader for that object may issue rays to model a diffuse reflection on that surface. As such, finding an intersection between a ray and a primitive is a step in determining whether and what kind of light energy may reach a pixel by virtue of a given ray, since what light is hitting that primitive still needs to be determined.
When a primitive has been found to be intersected by a ray, and the shader for that intersection is to be executed, an intersection point of the ray is defined based on where the intersection on the primitive was found. This intersection point is to serve as the origin for child rays that a shader may cast when shading this intersection.
This origin is usually implemented as a floating point tuplet of a certain precision, such as single precision or double precision. Primitives and other objects that generally can be intersected in a scene are smooth surfaces, which also “exist” at points in 3-space. Therefore, these scene surfaces exist at points in the scene that cannot be precisely represented by a number of a given precision. This is true whether single or double precision floating point is used (or even some more precise number interpretation), as computers are expected to have a capability to represent numbers only to a finite precision. This is true also for at least the practical reason that the more precise a number representation is to be, the more data is required to represent that number.
Therefore, the intersection point (origin for child rays) in the abstract represents a point on a surface determined to have been intersected by a ray, and ideally would serve as the exact origin for new rays resulting from shading. However, that point can only be expressed to a finite precision, and thus, the data describing the point, as it is stored, can have, and quite often does have, errors that place the intersection point off the surface of the scene that was intersected.
One problem that results is that child rays can have false intersections with the same primitive that their parent ray intersected. Another problem is that rays can fail to intersect a primitive that they were supposed to intersect. These intersection errors can result in rendering errors, such as gaps between portions of an object.
Currently, there are two known approaches to this problem, which is often called the “self-intersection” problem. One approach is to define a sphere of small radius around the intersection point (origin for child rays), and when a potential intersection is identified, a determination is made whether that intersection is outside of that sphere. This approach thus is directed to the situation where a child ray hits the same primitive as its parent.
Another approach to this problem has been to tag each child ray with information that identifies the origin primitive of that ray (i.e., the primitive whose shader spawned the child ray). Then, when a potential intersection for that child ray is identified, the potentially intersected primitive is compared with the origin primitive, and if they match, the intersection is rejected. A practical solution that uses this approach often can require further processing, such as an adjacent face rejection decision in which all vertices of a hit primitive are checked to see whether any of those vertices are in common with those of the origin primitive, and then the angle of incidence for the child ray is compared with that of the parent ray. Given that billions of intersection tests need to be performed in high quality ray tracing-based rendering, it is desirable to avoid these tests, which are relatively expensive to implement. These tests also do not necessarily give perfect results, as they are heuristic in nature.